<?php

abstract class Crud_Db_Table_Column_Abstract
{
	/**
	 * @var Crud_Db_Table_Abstract
	 */
	protected $_table;
	
	/**
	 * Column name in Db.
	 * 
	 * @var string
	 */
	protected $_name;
	
	/**
	 * Db type of column.
	 * 
	 * @var string
	 */
	protected $_type;
	
	/**
	 * Label for column.
	 * 
	 * @var string
	 */
	protected $_label = null;
	
	
	/**
	 * Flag for column is foreign key or not.
	 * 
	 * @var boolean
	 */
	protected $_isForeignKey;
	
	/**
	 * Flag for column is unique key or not.
	 * 
	 * @var boolean
	 */
	protected $_isUnique;
	
	/**
	 * Factory for creating columns. Take Crud_Table and column name.
	 * Use metadata for create needed column object.
	 * 
	 * @param Crud_Db_Table_Abstract $table
	 * @param string $name Column name
	 * @return Crud_Db_Table_Column_Abstract
	 */
	public static function factory(Crud_Db_Table_Abstract $table, $name)
	{
		$metadata = $table->getMetadata();
		$metadata = $metadata[$name];
		
		$type = $metadata['DATA_TYPE'];
		$type = preg_replace('/\(.+\)/', '', $type);
		switch ($type) {
			case 'enum' :
				$column = new Crud_Db_Table_Column_Enum($table, $name);
				break;
				
			case 'date' :
			case 'time' :
			case 'datetime' :
			case 'timestamp' :
				$column = new Crud_Db_Table_Column_Date($table, $name);
				break;
				
			default :
				$column = new Crud_Db_Table_Column($table, $name);
				break;
		}
		
		return $column;
	}
	
	/**
	 * Create column.
	 * 
	 * @param Crud_Db_Table_Abstract $table
	 * @param string $name Column name.
	 */
	public function __construct($table, $name)
	{
		if (!$table instanceof Crud_Db_Table_Abstract) {
			throw new Crud_Exception('Cant create column. Unknow table instance given.');
		}
		
		$this->_table = $table;
		$this->_name = $name;
	}
	
	/**
	 * Return parent for column table object.
	 * 
	 * @return Crud_Db_Table_Abstract
	 */
	public function getTable()
	{
		return $this->_table;
	}
	
	/**
	 * Return column name.
	 * 
	 * @return string
	 */
	public function getName()
	{
		return $this->_name;
	}
	
	public function getLabel()
	{
		if ($this->_label !== null) {
			return $this->_label;
		}
		
		$label = $this->getName();
		if ($config = $this->getConfig()) {
			if ($config->label) {
				$label = $config->label;
			}
		}
		
		$this->_label = $label;
		return $this->_label;
	}
	
	/**
	 * Return column type from Db.
	 * 
	 * @return string
	 */
	public function getType()
	{
		if ($this->_type) {
			return $this->_type;
		}
		
		$metadata = $this->_getMetadata();
		$this->_type = $metadata['DATA_TYPE'];
		return $this->_type;
	}
	
	/**
	 * Return column can be null or not.
	 * 
	 * @return boolean
	 */
	public function isNull()
	{
		$metadata = $this->_getMetadata();
		return $metadata['NULLABLE'];
	}
	
	/**
	 * Return max length of column.
	 * 
	 * @return integer
	 */
	public function getMaxLength()
	{
		$metadata = $this->_getMetadata();
		return $metadata['LENGTH'];
	}
	
	/**
	 * Return is column unsigned.
	 * 
	 * @return integer
	 */
	public function isUnsigned()
	{
		$metadata = $this->_getMetadata();
		return $metadata['UNSIGNED'];
	}
	
	/**
	 * Return true when column is foreign key.
	 * 
	 * @return boolean
	 */
	public function isFK()
	{
		if ($this->_isForeignKey !== null) {
			return $this->_isForeignKey;
		}
		
		$this->_isForeignKey = false;
		$map = $this->_getMap();
		if ($map !== null) {
			$this->_isForeignKey = true;
		}
		
		return $this->_isForeignKey;
	}
	
	/**
	 * Return table by foreign key.
	 * 
	 * @return Crud_Db_Table_Abstract
	 */
	public function getFKTable()
	{
		if (!$this->isFK()) {
			$name = $this->getName();
			throw new Crud_Exception("Column `$name` is not FK. Cant get FK table.");
		}
		
		$map = $this->_getMap();
		$class = get_class($this->getTable());
		$table = new $class($map['refTableClass']);
		return $table;
	}
	
	/**
	 * Get array of foreign key values.
	 * Key is ID. And value from reference table specified column.
	 * 
	 * @param integer $id
	 * @return array
	 */
	public function getFKValues($id = null)
	{
		$table = $this->getFKTable();
		$columnFK = $table->getPrimaryFKColumn();

		$select = $table->select();
		if ($id) {
			$select->where("`id` = ?", $id);
		}
		
		$select->order($columnFK);
		
		$values = array();
		$rowset = $table->fetchAll($select);
		foreach ($rowset as $row) {
			$values[ $row->id ] = $row->$columnFK;
		}
		
		return $values;
	}
	
	/**
	 * Get FK value by current column value.
	 * 
	 * @param integer $where
	 * @return string
	 */
	public function getFKValue($where)
	{
		$values = $this->getFKValues($where);
		return current($values);
	}
	
	/**
	 * Return true when column is unique key.
	 * 
	 * @return boolean
	 */
	public function isUnique()
	{
		if ($this->_isUnique !== null) {
			return $this->_isUnique;
		}
		
		$table = $this->getTable();
		$tableName = $table->getTableName();
		
		$adapter = $table->getAdapter();
        $sql = 'DESCRIBE ' . $adapter->quoteIdentifier($tableName, true);
        $stmt = $adapter->query($sql);

        $rowset = $stmt->fetchAll();
        $columnName = $this->getName();
		foreach ($rowset as $row) {
			if ($row['Field'] != $columnName) {
				continue;
			}
			
			$this->_isUnique = ($row['Key'] == 'UNI');
			return $this->_isUnique;
		}
		
		throw new Crud_Exception("Field `$columnName` is not exists in describe statement"); 
	}
	
	/**
	 * Return reference map for column if exists.
	 * 
	 * @return array|null
	 */
	protected function _getMap()
	{
		$maps = $this->getTable()->info(Zend_Db_Table_Abstract::REFERENCE_MAP);
		foreach ($maps as $map) {
			if ($map['columns'] == $this->getName()) {
				return $map;
			}
		}
		
		return null;
	}
	
	/**
	 * Get metadata for column.
	 * 
	 * @return array
	 */
	protected function _getMetadata()
	{
		$metadata = $this->getTable()->getMetadata();
		$metadata = $metadata[ $this->getName() ];
		return $metadata;
	}
	
	/**
	 * Return true when column need to show is list.
	 * 
	 * @return boolean
	 */
	public function showInList()
	{
		$config = $this->getConfig();
		if (!$config) {
			return false;
		}
		
		return $config->list;
	}
	
	/**
	 * Return true when column need to show is item.
	 * 
	 * @return boolean
	 */
	public function showInItem()
	{
		$config = $this->getConfig();
		if (!$config) {
			return false;
		}
		
		return $config->item;
	}
	
	/**
	 * Return column configuration.
	 * 
	 * @return Zend_Config
	 */
	public function getConfig()
	{	
		$config = $this->getTable()->getConfig();
		if (!$config) return null;
		$name = $this->getName();
		$config = $config->$name;
		return $config;
	}
	
	/**
	 * Return array of filters for column.
	 * 
	 * @return array
	 */
	public function getFilters()
	{
		return array();
	}
	
	/**
	 * Return array of validators for column.
	 * 
	 * @return array
	 */
	public function getValidators()
	{
		return array();
	}
}